﻿using System;
using System.ComponentModel.Composition;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;
using AxeFrog.Net.Mail;
using AxeFrog.Security;

namespace AxeFrog.Web
{
	/// <summary>
	/// Encapsulates the login status for the current user, including logging in, testing login status,
	/// logging out and login cookie handling. Also provides a reference to the currently-logged-in user
	/// record and any roles related to it.
	/// </summary>
	[Export]
	public class UserContext
	{
		private readonly Mailer _mailer;
		private User _user;
		private AccessLevel _accessLevel;
		private long[] _roles;

		public User User { get { LoadUser(); return _user; } }
		public AccessLevel AccessLevel { get { LoadUser(); return _accessLevel; } }
		public long[] Roles { get { LoadUser(); return _roles; } }
		public string Signature { get; private set; }

		private HttpContext HttpContext { get { return HttpContext.Current; } }
		private HttpCookie Cookie { get; set; }

		[Import(AllowDefault = false)]
		private IAccountAccessHandler AccountAccessHandler { get; set; }

		[ImportingConstructor]
		public UserContext(Mailer mailer)
		{
			_mailer = mailer;
		}

		private bool _loaded;
		private void LoadUser()
		{
			if(_loaded)
				return;
			_loaded = true;

			if(AccountAccessHandler != null)
			{
				HttpCookie cookie = HttpContext.Request.Cookies["AxeFrog.UserContext"];
				if(cookie == null) return;
				long id = (cookie["id"] ?? "").ToLong();
				if(id > 0)
				{
					Signature = cookie["sig"] ?? "";
					AccountAccessHandler.VerifyLoginStatus(id, Signature, Salt, out _user, out _roles, out _accessLevel);
				}
			}
		}

		public bool Register(string name, string username, string password, string email, int timezoneOffset, string emailSubject, string emailTemplate)
		{
			_user = AccountAccessHandler.RegisterNewUser(name, username, password, email, timezoneOffset);
			if(_user != null && Login(username, password))
			{
				_accessLevel = AccessLevel.Authenticated;
				_roles = new long[0];
				var sent = AccountAccessHandler.SendAccountEmail(_user.ID, emailSubject,
					WebsiteSettings.SupportEmailName, WebsiteSettings.SupportEmailAddress,
					WebsiteSettings.MailServerHost, WebsiteSettings.MailServerPort,
					WebsiteSettings.MailServerUsername, WebsiteSettings.MailServerPassword,
					emailTemplate);

				if(sent)
					return Login(username, password);
			}
			return false;
		}

		public bool Login(string username, string password)
		{
			return Login(username, password, out _user);
		}

		public bool Login(string username, string password, out User user)
		{
			user = null;

			if(AccountAccessHandler == null)
				return false;

			string sig;
			if(AccountAccessHandler.Login(username, password, Salt, out user, out _roles, out sig, out _accessLevel))
			{
				_user = user;
				_loaded = true;
				Signature = sig;
				UpdateCookie();
				return true;
			}

			return false;
		}

		public bool Login(long userID, string signature)
		{
			return Login(userID, signature, out _user);
		}

		public bool Login(long userID, string signature, out User user)
		{
			user = null;

			if(AccountAccessHandler == null)
				return false;

			if(AccountAccessHandler.VerifyLoginStatus(userID, signature, Salt, out user, out _roles, out _accessLevel))
			{
				_user = user;
				_loaded = true;
				Signature = signature;
				UpdateCookie();
				return true;
			}

			return false;
		}

		public void Logout()
		{
			_user = null;
			_accessLevel = AccessLevel.Anonymous;
			_roles = null;
			UpdateCookie();
		}

		private void UpdateCookie()
		{
			HttpCookie cookie = Cookie ?? HttpContext.Request.Cookies["AxeFrog.UserContext"];
			if(_accessLevel == AccessLevel.Anonymous)
			{
				if(cookie != null)
				{
					cookie.Expires = DateTime.UtcNow.AddDays(-30);
					HttpContext.Response.Cookies.Add(cookie);
				}
			}
			else
			{
				if(cookie == null)
					Cookie = cookie = new HttpCookie("AxeFrog.UserContext");
				if(!HttpContext.Response.Cookies.AllKeys.Contains(cookie.Name))
					HttpContext.Response.Cookies.Add(cookie);
				cookie.Expires = DateTime.UtcNow.AddYears(1);
				cookie["sig"] = Signature;
				cookie["id"] = User.ID.ToString();
			}
		}

		public bool ChangePassword(string newPassword)
		{
			LoadUser();
			_user.SetPassword(newPassword, WebsiteSettings.PublicKey);
			return AccountAccessHandler.UpdateUser(_user, Signature, Salt);
		}

		public static string Salt
		{
			get
			{
				string salt = null;
				if(WebsiteSettings.SaltKey != null)
					salt = HttpContext.Current.Request.Headers[WebsiteSettings.SaltKey];
				return salt ?? HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
			}
		}

		public bool HasRole(long userRole)
		{
			LoadUser();
			return _accessLevel == AccessLevel.SuperUser || Roles.Contains(userRole);
		}

		public bool HasRole(object userRole)
		{
			LoadUser();
			if(!userRole.GetType().IsEnum)
				throw new ArgumentException("userRole should be an enumerated constant");
			return _accessLevel == AccessLevel.SuperUser || Roles.Contains(Convert.ToInt64(userRole));
		}

		public bool UpdateUser()
		{
			LoadUser();
			return AccountAccessHandler.UpdateUser(_user, Signature, Salt);
		}

		public bool SendPasswordReminder(string email)
		{
			return AccountAccessHandler.SendAccountEmail(email, "Password Reminder",
				WebsiteSettings.SupportEmailName, WebsiteSettings.SupportEmailAddress,
				WebsiteSettings.MailServerHost, WebsiteSettings.MailServerPort,
				WebsiteSettings.MailServerUsername, WebsiteSettings.MailServerPassword,
				WebsiteSettings.GetEmailTemplate(EmailTemplate.PasswordReminder).ReplaceTokens("[", "]", new
				{
					WEBSITETITLE = WebsiteSettings.DefaultCompanyName,
					SUPPORTEMAIL = WebsiteSettings.SupportEmailAddress
				}));
		}
	}
}